package com.zy.ems.common.util;

import java.lang.management.ManagementFactory;
import java.util.Calendar;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zy.ems.common.cache.MemcachedUtils;
import com.zy.ems.common.exception.BaseErrorStatus;
import com.zy.ems.common.exception.BusinessException;

/**
 * 序列号工具类。
 * 
 * @author Luffy
 *
 */
public class SNUtils {

    private static Logger logger = LoggerFactory.getLogger(SNUtils.class);

    private static final String paySnKeyPre = "paySnKeyPre_";

    // 生成随机用户名
    private static final String userSnKeyPre = "userSnKeyPre_";

    private static String pid = "00000";

    private static AtomicLong maxPaySeq = new AtomicLong(0);

    private static AtomicLong nowPaySeq = new AtomicLong(0);

    private static AtomicLong maxUserSeq = new AtomicLong(0);

    private static AtomicLong nowUserSeq = new AtomicLong(0);

    private static String snDate = "";

    static {
        try {
            pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
            if (pid == null || "".equals(pid)) {
                pid = "00000";
            }
            else if (pid.length() <= 5) {
                pid = String.format("%05d", Integer.parseInt(pid));
            }
            else {
                pid = pid.substring(pid.length() - 5, pid.length());
            }
        }
        catch (Exception e) {
            pid = "00000";
        }
    }

    /**
     * 生成自定义长度的sn，需要自己去重。
     * 
     * @param length
     * @return
     * @author zhupengren
     * @date 2015年5月27日 下午4:17:43
     */
    public static Long createCustomSn(Integer length) {
        return createDefultSn(length);
    }

    public static Long createUserSn(int length) {
        if (length < 10) {
            throw new RuntimeException("用户序号必须大于等于10 位！！！");
        }
        String yymmdd = getYymmddStr();
        Long userSn = null;
        long sn = 0;
        try {
            if (maxUserSeq.get() < (sn = nowUserSeq.incrementAndGet())) {
                synchronized (nowUserSeq) {
                    if (maxUserSeq.get() < nowUserSeq.get()) {
                        maxUserSeq = new AtomicLong(MemcachedUtils
                                .addOrIncr(userSnKeyPre + yymmdd, 50));
                        nowUserSeq = new AtomicLong(maxUserSeq.get() - 10);
                    }
                    sn = nowUserSeq.incrementAndGet();
                }
            }
        }
        catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        if (sn > 0L) {
            userSn = Long.parseLong(
                    yymmdd + String.format("%0" + (length - 6) + "d", sn));
        }
        return userSn;
    }

    public static Long getSn(String date) {
        long sn = 0;
        try {
            if (!snDate.equals(date)
                    || maxPaySeq.get() < (sn = nowPaySeq.incrementAndGet())) {
                synchronized (nowPaySeq) {
                    if (!snDate.equals(date)
                            || maxPaySeq.get() < nowPaySeq.get()) {
                        snDate = date;
                        maxPaySeq = new AtomicLong(MemcachedUtils
                                .addOrIncr(paySnKeyPre + date, 10));
                        nowPaySeq = new AtomicLong(maxPaySeq.get() - 10);
                    }
                    sn = nowPaySeq.incrementAndGet();
                }
            }
        }
        catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return sn;
    }

    /**
     * 默认生成序列号方法，序列号规则为：yyMMdd(6) + pid(5) + sn(n)
     * 
     * @param length 要生成sn的长度；0或null，表示按默认规则生成大于12位的可变长度sn。
     * @return
     * @author zhupengren
     * @date 2015年5月27日 上午11:57:38
     */
    public static Long createDefultSn(Integer length) {
        long defultSn = 0;
        String yymmdd = getYymmddStr();
        long sn = getSn(yymmdd);

        if (sn <= 0L) {
            logger.warn("获取缓存序列失败！！！！！！！！！！！！！！！！！！！！");
            sn = System.currentTimeMillis() % 1000;
        }
        if (length != null && length > 0) {
            if (length > 11) {
                defultSn = Long.parseLong(yymmdd + pid
                        + String.format("%0" + (length - 11) + "d", sn));
            }
            else {
                defultSn = Long.parseLong(
                        yymmdd + String.format("%0" + (length - 6) + "d", sn));
            }
        }
        else {
            defultSn = Long.parseLong(yymmdd + pid + sn);
        }
        if (defultSn == 0) {
            logger.error("获取序列失败！！！！！！！！！！！！！！！！！！！！");
            throw new BusinessException(BaseErrorStatus.sys_003);
        }
        return defultSn;
    }

    /**
     * 获取当前时间的yyMMdd格式的字符串。
     * 
     * @return
     */
    private static String getYymmddStr() {
        Calendar ca = Calendar.getInstance();
        String year = String.format("%04d", ca.get(Calendar.YEAR)).substring(2);
        String month = String.format("%02d", ca.get(Calendar.MONTH) + 1);
        String day = String.format("%02d", ca.get(Calendar.DATE));
        return year + month + day;
    }

}
